/*
 * TcpServer.cpp
 *
 *  Created on: Aug 12, 2016
 *      Author: ubuntu
 */

#include "TcpServer.h"
#include "ClientConnect.h"
#include "common.h"
#include <syslog.h>
#include <errno.h>

TcpServer::TcpServer(int port, int type) : m_port(port), m_type(type)
{
	m_fd = -1;
	m_epfd = -1;
	m_bTerminated = false;
	ep_ev = NULL;
	event_cnt = NEVENTS;
	m_op = NULL;
}

TcpServer::~TcpServer()
{
	if (ep_ev)
		free(ep_ev);

	if (m_fd > 0)
		close(m_fd);

	if (m_epfd > 0)
		close(m_epfd);

	if (m_op)
		free(m_op);
	list<ClientConnection *>::iterator it = m_lstClient.begin();
	for (; it != m_lstClient.end(); it++) {
		ClientConnection *pConn = *it;
		pConn->on_close();
		delete pConn;
	}
}

bool TcpServer::init()
{
	struct sockaddr_in saddr;
	struct epoll_event event;
	m_fd = socket(AF_INET, SOCK_STREAM, 0);
	if (m_fd < 0) {
		return false;
	}
	bzero(&saddr, sizeof(saddr));
	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(m_port);
	saddr.sin_addr.s_addr = INADDR_ANY;

	int on = 1;

	setsockopt(m_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
	if (bind(m_fd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) {
		close(m_fd);
		m_fd = -1;
		return false;
	}

	if (listen(m_fd, 4096) < 0) {
		close(m_fd);
		m_fd = -1;
		return false;
	}

	setnonblocking(m_fd);

	m_epfd = epoll_create(32000);
	if (m_epfd < 0) {
		close(m_fd);
		m_fd = -1;
		return false;
	}

	ep_ev = (struct epoll_event *)calloc(NEVENTS, sizeof(struct epoll_event));
	if (!ep_ev) {
		close(m_epfd);
		close(m_fd);
		m_fd = -1;
		m_epfd = -1;
		return false;
	}

	m_op = (struct connectop *)malloc(sizeof(struct connectop));
	if (!m_op) {
		close(m_epfd);
		close(m_fd);
		return false;
	}

	m_op->fd = m_fd;
	m_op->ptr = (void *)this;
	m_op->epfd = m_epfd;
	m_op->dispatch = TcpServer::on_accept;

	event.events = EPOLLIN;
	event.data.ptr = m_op;
	epoll_ctl(m_epfd, EPOLL_CTL_ADD, m_fd, &event);

	return true;
}

void TcpServer::start()
{
	debug(LOG_DEBUG, "Start to run server");
	run();
	debug(LOG_DEBUG, "Stop to run");
	sleep(2);
}

void TcpServer::stop()
{
	m_bTerminated = true;
}

void TcpServer::run()
{
	int res;
	while (!m_bTerminated) {
		res = epoll_wait(m_epfd, ep_ev, event_cnt, 10);
		if (res == -1) {
			if (errno != EINTR) {
				m_bTerminated = false;
				return;
			}
			continue;
		}
		if (res == 0)
			continue;
		for (int i = 0; i < res; i++) {
			struct connectop *op = (struct connectop *)(ep_ev[i].data.ptr);
			op->dispatch(op->ptr);
		}
		if (res == event_cnt && event_cnt < MAX_EVENTS) {
			int new_nevents = event_cnt * 2;
			struct epoll_event *new_events = (struct epoll_event *)realloc(ep_ev, new_nevents * sizeof(struct epoll_event));
			if (new_events) {
				debug(LOG_DEBUG, "当前事件: %d", new_nevents);
				event_cnt = new_nevents;
				ep_ev = new_events;
			}
		}
	}
}

int TcpServer::getPort()
{
	return m_port;
}

int TcpServer::on_accept(void *arg)
{
	TcpServer *pServer = (TcpServer *)arg;
	return pServer->do_accept();
}

int TcpServer::on_read(void *arg)
{
	ClientConnection *pConnect = (ClientConnection *)arg;
	pConnect->on_read();
	return 1;
}

int TcpServer::do_accept()
{
	int fd;
	struct sockaddr addr;
	socklen_t alen = sizeof(addr);
	struct epoll_event event;
	struct connectop *op = (struct connectop *)malloc(sizeof(struct connectop));
	if (!op) {
		debug(LOG_ERR, "malloc failed: %s", strerror(errno));
		exit(1);
	}
	while ((fd = accept(m_fd, (struct sockaddr *)&addr, &alen)) < 0) {
		if (errno == EINTR)
			continue;
		m_bTerminated = true;
		free(op);
		return -1;
	}

	setnonblocking(fd);
	op->fd = fd;
	op->epfd = m_epfd;
	op->dispatch = TcpServer::on_read;
	ClientConnection *pConnect = on_connect(op, (struct sockaddr *)&addr);
	op->ptr = (void *)pConnect;
	m_lstClient.push_back(pConnect);

	event.events = EPOLLIN|EPOLLET;
	event.data.ptr = op;
	epoll_ctl(m_epfd, EPOLL_CTL_ADD, fd, &event);
	return 1;
}

ClientConnection *TcpServer::on_connect(struct connectop *op, struct sockaddr *addr)
{
	ClientConnection *pConnect = new ClientConnection(this, op, addr);
	return pConnect;
}

void TcpServer::on_close(ClientConnection *pConnect)
{
	m_lstClient.remove(pConnect);
	pConnect->on_close();
	delete pConnect;
}

void TcpServer::add(void *arg)
{
}

void TcpServer::del(void *arg)
{
}

void *TcpServer::find(void *arg)
{
	return NULL;
}

void TcpServer::clearClient()
{
	if (m_type == SV_CMD)
		return;
	if (m_lstClient.empty())
		return;
	time_t ts = time(NULL);
	list<ClientConnection *>::iterator it = m_lstClient.begin();
	for (; it != m_lstClient.end(); ) {
		ClientConnection *pConnect = *it;
		if ((ts > pConnect->getLastTime()) && (ts - pConnect->getLastTime() > 300)) {
			pConnect->on_close();
			delete pConnect;
			it = m_lstClient.erase(it);
		} else {
			it++;
		}
	}
}
